package com.wuwei.tms.workbench.service.impl;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.wuwei.tms.pojo.*;
import com.wuwei.tms.setting.mapper.UserMapper;
import com.wuwei.tms.utils.result.Result;
import com.wuwei.tms.vo.BorrowExcel;
import com.wuwei.tms.vo.BorrowPrint;
import com.wuwei.tms.vo.BorrowVo;
import com.wuwei.tms.workbench.mapper.BorrowMapper;
import com.wuwei.tms.workbench.mapper.HistoryMapper;
import com.wuwei.tms.workbench.mapper.ToolMapper;
import com.wuwei.tms.workbench.service.BorrowService;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

@Service
public class BorrowServiceImpl implements BorrowService {

    @Resource
    private ToolMapper toolMapper;

    @Resource
    private BorrowMapper borrowMapper;



    @Resource
    private UserMapper userMapper;



    @Resource
    private HistoryMapper historyMapper;


    /**
     ** 执行工具借还操作的业务
     ** 1 为了避免员工编号和工具编号输入错误，先做一下判断
     ** 2 判断工具是消耗品还是非消耗品，因为消耗品要做数量加减，非消耗品不用
     ** 2.1当不是消耗品时，通过在borrow表中根据传来的barcode查询有无borrow执行借用还是归还操作
     ** 2.2执行借用操作时对工具的状态做了判断，以及当工具为计量工具也做了判断
     ** 2.3执行归还操作时可以帮别人还工具，将信息封装到 Message发给controller
     ** 3 当工具时消耗品时执行谁借的谁来还的原则
     ** 3.1借用消耗品时会将借用的数量从tool表中useableNum扣除，并根据useableNum数量对借用数量做了限制
     ** 3.2归还的之后将归还的数量增加到tool表中useableNum，对于归还数量不需要限制
     * @param borrowVo 前端传来的对象
     * @param operator 操作者的工号，从request中的请求头中获得
     */
    @Override
    @Transactional
    public Result<String> borrowAndReturn(BorrowVo borrowVo, Long operator) {
        Long empno = borrowVo.getEmpno();
        LocalDateTime now = LocalDateTime.now();
        //通过empno查询到t_user表中的主键
        LambdaQueryWrapper<User> queryWrapper1 = new LambdaQueryWrapper<>();
        queryWrapper1.eq(empno!=null,User::getEmpno,empno);
        User user = userMapper.selectOne(queryWrapper1);
        if (user == null) {
            return Result.fail("员工编号输入错误！");
        }

        //从工具表中查出toolid对应的工具
        Long barcode = borrowVo.getBarcode();
        LambdaQueryWrapper<Tool> queryWrapper2 = new LambdaQueryWrapper<>();
        queryWrapper2.eq(Tool::getBarcode,barcode);
        Tool tool = toolMapper.selectOne(queryWrapper2);
        if (tool == null) {
            return Result.fail("查无此工具！");
        }

        Integer consumeFlag = tool.getConsumeFlag();//通过toolid查出该工具是消耗品还是工具

        if (consumeFlag!=3&&consumeFlag!=4){//如果不是消耗品或者航材，查询该工具有没有人借过，有人借过就执行归还操作，没有人借过就执行借用操作
            LambdaQueryWrapper<Borrow> queryWrapper3 = new LambdaQueryWrapper<>();
            queryWrapper3.eq(tool.getBarcode()!=null,Borrow::getBarcode,tool.getBarcode());
            Borrow borrow = borrowMapper.selectOne(queryWrapper3);

            if (borrow==null){//如果查到borrow为空，表明没有人借过这个工具，执行借用操作
                return borrowNoConsume(borrowVo, operator, now, barcode, tool);
            }

            else {//如果查到borrow不为空，表明有人借过这个工具，执行还工具操作
                return returnNoConsume(operator, now, empno, borrow,borrowVo);
            }
        }

        else {//如果是消耗品或者航材，则不需要判断是否有人借过该工具，因为消耗品是不能由别人还的

            //在t_borrow表中根据userId查出此人有没有借出过这个工具
            LambdaQueryWrapper<Borrow> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(Borrow::getBorrowById,empno).eq(Borrow::getBarcode,barcode);
            Borrow borrow = borrowMapper.selectOne(queryWrapper);
            if (borrow==null){//如果查到borrow为空表明此人没有借过该工具，执行借用操作
                return borrowConsume(borrowVo, operator, now, barcode, tool);
            }
            else {//如果查到borrow不为空表明此人有借过该工具，执行还工具操作.
                //通过empno查询到t_user表中的主键
                return returnConsume(borrowVo, operator, now, tool, borrow);
            }
        }

    }

    /**
     * 借还管理页面查询用于展示的
     * @param currentPage
     * @param pageSize
     * @param search
     * @return
     */
    @Override
    public List<Borrow> listAndPagination(Integer currentPage, Integer pageSize, String search) {
        int offset = (currentPage-1)*pageSize;
        List<Borrow> borrowList = borrowMapper.mySelectPage(offset,pageSize,search);
        return borrowList;
    }



    @Override
    public  List<BorrowExcel>  unReturnedPage(Integer currentPage, Integer pageSize, Long empno, String airplane) {
        List<BorrowExcel> borrowList = new ArrayList<>();
        int offset = (currentPage-1)*pageSize;
        if(empno==0&&"_".equals(airplane)){
            return borrowList;
        }
        airplane = "_".equals(airplane)?"":airplane;
        borrowList = borrowMapper.myUnreturnedPage(offset,pageSize,empno,airplane);
        findUserName(borrowList);
        return borrowList;
    }


    private void findUserName(List<BorrowExcel> borrowList) {
        if (borrowList.size()==0) {
            return;
        }else {
            List<Long> returnIds = new ArrayList<>();
            List<Long> borrowIds = new ArrayList<>();
            borrowList.forEach(b->{
                returnIds.add(b.getReturnById());
                borrowIds.add(b.getBorrowById());
            });

            List<User> returnUsers = new ArrayList<>();
            List<User> borrowUsers = new ArrayList<>();

            if (returnIds.size()>0) {
                returnUsers = userMapper.selectBatchEmpnos(returnIds);
            }
            if (borrowIds.size()>0) {
                borrowUsers = userMapper.selectBatchEmpnos(borrowIds);
            }

            List<User> finalBorrowUsers = borrowUsers;
            List<User> finalReturnUsers = returnUsers;

            borrowList.forEach(h->{
                Long borrowById = h.getBorrowById();
                Optional<User> borrowUser  = finalBorrowUsers.stream().filter(u -> u.getEmpno().equals(borrowById)).findFirst();
                borrowUser.ifPresent(user -> h.setBorrowByName(user.getName()));

                Long returnById = h.getReturnById();
                Optional<User> returnUser = finalReturnUsers.stream().filter(u -> u.getEmpno().equals(returnById)).findFirst();
                returnUser.ifPresent(user -> h.setReturnByName(user.getName()));
            });
        }
    }

    @Override
    public List<BorrowExcel> downloadUnReturned(Long empno, String airplane) {
        List<BorrowExcel> records = new ArrayList<>();
        if(empno==0&&"_".equals(airplane)){
            return records;
        }
        airplane = "_".equals(airplane)?"":airplane;
        records = borrowMapper.downloadUnReturned(empno,airplane);
        findUserName(records);
        return records;
    }

    /**
     * 用于提供给打印机以需要打印的内容的方法
     * @param empno
     * @return
     */
    @Override
    public List<BorrowPrint> printForList(Long empno) {
        List<BorrowPrint> list = borrowMapper.printForList(empno);
        return list;
    }

    /**
     * 归还消耗品或者航材的方法，这里tool的consumeFlag是3或者4
     * @param borrowVo 前端传来的数据,里面包含员工号,飞机号借用数量,工具编号
     * @param operator 操作者的工号,从request的请求头中获取
     * @param now 系统当前时间
     * @param tool 数据库查询来的工具实体类
     * @param borrow 数据库查询到的borrow对象
     * @return
     */
    private Result<String> returnConsume(BorrowVo borrowVo, Long operator, LocalDateTime now, Tool tool, Borrow borrow) {
        borrow.setReturnById(borrowVo.getEmpno());
        borrow.setReturnTime(now);
        borrow.setOperator(operator);
        Integer returnCount = borrow.getReturnCount();//先得到此工具之前已经还过的数量,此条数据会随着每次归还增加,直到借出数量等于归还数量
        Integer borrowCount = borrow.getBorrowCount();//该条记录为借出后的总工具数,此条数据在每次归还操作时不会改变
        //如果归还数量大于借出数量，只能还最大借出数
        Integer toolnum = borrowVo.getToolnum();
        /**
         * returnCount如果不等于0,代表已经还过一批,此时归还数量应该是原来还的数量加上这次还的数量
         * 如果数据更改后借出数量与更换数量一样,代表所有消耗品已经还掉了
         * 举例:
         * 如果借了20条抹布,此时数据中的借出数量为20,归还数量为0
         * 第一次还了5条,那么就把归还数量改成5,
         * 如果下次再还的时候发现已经有归还数量了,那就在5的基础上增加
         * 当某次归还数量也等于20时,代表所有消耗品都还完了
         * 那么如果借了20条,之前换了5条,这次还16条时,就会触发toolnum>borrowCount-returnCount
         * 此时toolnum就会变成20-5=15
         **/

        /**
         *这里当归还消耗品或航材时需要做以下几件事
         * 1.改库存，此时，如果是消耗品，归还时总数不用修改，修改可用数量就可以，比如一共有20条抹布，借出去了10条，此时总数是20，可用数量是10
         * 此时归还了5条。将可用数量改成5即可，此时为总数20，可用数量15
         * 2、如果是航材，比如说有20个插钉，借出去了10个，此时可用数量是10，归还了5个，不用改可用数量，因为归还的也用不了了，修改总数变成20-5=15即可
         * 3。如果归还数量等于借用数量，则表明均已归还，此时还要添加一条历史记录、
         */
        if (toolnum>borrowCount-returnCount){
            toolnum = borrowCount-returnCount;
        }
        borrow.setReturnFlag(1);
        borrow.setReturnCount(returnCount+toolnum);
        borrow.setOperator(operator);
        borrow.setOperatorName(borrowVo.getName());
        String str ="您已归还"+toolnum+"件工具!";

        //此时还要将tool中的库存进行修改
        Integer consumeFlag = tool.getConsumeFlag();
        //如果consumeFlag是3，表示是消耗品，归还了几个工具，就在工具可用数量上加上归还的数量
        if (consumeFlag==3) {
            tool.setUserableNum(tool.getUserableNum()+toolnum);
            toolMapper.updateById(tool);
        } else {//如果consumeFlag是4，表示是航材，归还了几个工具，就在工具总数上减去归还的数量
            tool.setAllNumber(tool.getAllNumber()-toolnum);
            toolMapper.updateById(tool);
        }

        //如果归还数量和借出数量相等，表明所有的消耗品或者航材都已经归还
        if (borrow.getReturnCount().equals(borrowCount)){
            //这里当这里的工具还完了，该记录就被清空了，此时向t_history表中增加一条数据
            History history = new History();
            BeanUtils.copyProperties(borrow,history);
            history.setOperatorName(borrowVo.getName());
            history.setOperator(operator);
            historyMapper.insert(history);
            borrowMapper.deleteById(borrow);
            return Result.ok("工具归还成功!");
        }
        borrowMapper.updateById(borrow);//执行归还操作就是向t_borrow表中修改一条借用记录

        return Result.ok(str);
    }

    /**
     * 借用消耗品的操作，这里tool的consumeFlag是3或者4
     * @param borrowVo 前端传来的数据,里面包含员工号,飞机号借用数量,工具编号
     * @param operator 操作者的工号,从request的请求头中获取
     * @param now 系统当前时间
     * @param barcode 工具编号
     * @param tool 数据库查询来的工具实体类
     * @return
     */
    private Result<String> borrowConsume(BorrowVo borrowVo, Long operator, LocalDateTime now,
                                         Long barcode, Tool tool) {
        if (tool.getStatus()!=0) {//如果借的工具是报废或者隔离状态，则不允许借出
//                    return Result.fail("借用失败！该工具状态为"+tool.getStatus());
            String str = tool.getStatus()==1?"隔离":"报废";
            return Result.fail("借用失败！该工具状态为"+str);
        }
        Borrow borrow;//如果借用工具数量大于工具的库存数量，则借用失败！
        Long empno = borrowVo.getEmpno();
        Integer userableNum = tool.getUserableNum();
        if (borrowVo.getToolnum()>userableNum){
            return Result.fail("借用失败!该工具现只有:"+userableNum+"!借用数量不能大于库存数量");
        }

        borrow = new Borrow();
        borrow.setAirplaneId(borrowVo.getAirplane());//因为borrow表中airplaneId是外键，关联airplane的主键
        borrow.setBorrowById(empno);
        borrow.setBorrowCount(borrowVo.getToolnum());
        borrow.setBorrowTime(now);
        borrow.setReturnFlag(0);
        borrow.setBarcode(barcode);
        borrow.setOperator(operator);
        borrow.setToolName(tool.getName());
        borrow.setOperatorName(borrowVo.getName());
        borrowMapper.insert(borrow);//执行借用操作就是向t_borrow表中创建一条借用记录

        //此时还要将tool中的库存进行修改,现在的库存是可用的数量减去借用的数量
        tool.setUserableNum(userableNum-borrowVo.getToolnum());
        toolMapper.updateById(tool);
        return Result.ok("工具借用成功");
    }

    /**
     * 归还非消耗品的工具的方法，这里tool的consumeFlag是1或者2
     * @param operator 操作者的工号,从request的请求头中获取
     * @param now 系统当前时间
     * @param empno 员工工号
     * @param borrow 数据库查询到的borrow对象
     * @return
     */
    private Result<String> returnNoConsume(Long operator, LocalDateTime now, Long empno, Borrow borrow,BorrowVo borrowVo) {
        //如果归还者和tool的借用者不是一个人，表明他是帮别人还工具的，要给前端一个alert

//                if (!borrow.getBorrowById().equals(borrow.getReturnById())) {
//                    //帮别人还也是可以的，但是要给前端一个提示
//                    Long borrowById = borrow.getBorrowById();
//                    //通过借用者的Id查出借用人的名称
//                    User borrowUser = userMapper.getUserListById(borrowById);
//                    return Result.ok("\"您帮\"+borrowUser.getName()+\"还了工具!  :\"+tool.getName()");
//                }
        //borrowMapper.updateById(borrow);
        //这里当这里的工具还完了，该记录就被清空了，此时向t_history表中增加一条数据
        History history = new History();
        BeanUtils.copyProperties(borrow,history);
        history.setReturnById(empno);
        history.setReturnTime(now);
        history.setReturnCount(1);
        history.setOperator(operator);
        history.setOperatorName(borrowVo.getName());
        historyMapper.insert(history);
        borrowMapper.deleteById(borrow);
        if (!history.getBorrowById().equals(history.getReturnById())){
            return Result.ok("你帮"+history.getBorrowById()+"归还了工具:"+history.getToolName());
        }
        return Result.ok("工具归还成功!");
    }

    /**
     * 借用非消耗品的工具的方法，这里tool的consumeFlag是1或者2
     * @param borrowVo 前端传来的数据,里面包含员工号,飞机号借用数量,工具编号
     * @param operator 操作者的工号,从request的请求头中获取
     * @param now 系统当前时间
     * @param barcode 工具的编号
     * @param tool 工具对象
     * @return
     */
    private Result<String> borrowNoConsume(BorrowVo borrowVo, Long operator, LocalDateTime now, Long barcode, Tool tool) {
        if (tool.getStatus()!=0) {//如果借的工具是报废或者隔离状态，则不允许借出
//                    return Result.fail("借用失败！该工具状态为"+tool.getStatus());
            String str = tool.getStatus()==1?"隔离":"报废";
            return Result.fail("借用失败！该工具状态为"+str);
        }

        //如果工具是计量工具，在借用之前还要判断该工具是否到期
        if (tool.getConsumeFlag()==1) {
            LocalDate expireDate = tool.getExpireDate();
            LocalDate currentDate = LocalDate.now(); // 获取当前日期
            //新创建的工具的expireTime都是null，因此可以通过null来判断该工具是否可以借出
            if (expireDate == null) {
                return Result.fail("该工具还没有到期日期，请通知工具管理员!");
            }

            if (expireDate.isBefore(currentDate)) {//如果当前时间比到期时间早说明该工具已经过期
                //这里还是不要在系统中自动隔离了
//                tool.setStatus(1);
                toolMapper.updateById(tool);
                return Result.fail("该工具已经过期！请通知工具管理员将此工具隔离!");
            }

        }
        //否则执行借用操作
        Borrow borrow1 = new Borrow();
        borrow1.setAirplaneId(borrowVo.getAirplane());//因为borrow表中airplaneId是外键，关联airplane的主键
        borrow1.setBorrowById(borrowVo.getEmpno());//
        //因为不是消耗品，每个barcode都只能对应一个工具，所以不管前端借的数量是多少，都设置borrowcount为1即可
        borrow1.setBorrowCount(1);//因为工具是按barcode划分的，属于一一对应关系，借就肯定只能借一个
        borrow1.setBorrowTime(now);
        borrow1.setReturnFlag(0);
        borrow1.setBarcode(barcode);
        borrow1.setOperator(operator);
        borrow1.setOperatorName(borrowVo.getName());
        borrow1.setToolName(tool.getName());
        borrowMapper.insert(borrow1);//执行借用操作就是向t_borrow表中创建一条借用记录
        return Result.ok("工具借用成功!");
    }
}
